package com.wlyuan.open.domain.source.dispatcher.conversion;

import com.wlyuan.basic.api.ICarrierService;
import com.wlyuan.basic.api.ICustomerService;
import com.wlyuan.basic.dto.consignee.ConsigneeDetailDTO;
import com.wlyuan.open.constants.CarrierTypeEnum;
import com.wlyuan.open.constants.CustomerTypeEnum;
import com.wlyuan.open.domain.source.SourceCarrier;
import com.wlyuan.open.domain.source.SourceCustomer;
import com.wlyuan.open.domain.source.dispatcher.consignee.CreateConsignee;
import com.wlyuan.open.domain.source.repository.cache.*;
import com.wlyuan.open.dto.order.OrderGoodsCreateDTO;
import com.wlyuan.open.remote.facade.Address;
import com.wlyuan.open.remote.facade.AddressDTO;
import com.wlyuan.open.remote.facade.AreaFacade;
import com.wlyuan.open.remote.facade.ConsigneeFacade;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import lombok.var;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * @author lijing
 * @description
 */
@RequiredArgsConstructor
@Service
@Slf4j
public class BaseOpenDispatcherConversion {
    private final OpenTenantCacheRepository openTenantCacheRepository;
    private final OpenCustomerCacheRepository openCustomerCacheRepository;
    private final OpenUserCacheRepository openUserCacheRepository;
    private final OpenGoodsCacheRepository openGoodsCacheRepository;
    private final OpenCarrierCacheRepository openCarrierCacheRepository;
    private final OpenTransportCacheRepository openTransportCacheRepository;
    private final OpenCombineCacheRepository openCombineCacheRepository;
    private final OpenTemplateCacheRepository openTemplateCacheRepository;
    private final ICarrierService carrierService;
    private final ICustomerService customerService;
    private final AreaFacade areaFacade;
    private final ConsigneeFacade consigneeFacade;

    /**
     * 租户
     *
     * @param tenantId
     * @return
     */
    public Long tenant(Long tenantId) {
        return openTenantCacheRepository.convert(tenantId);
    }

    /**
     * 用户
     *
     * @param userId
     * @return
     */
    public Long user(Long userId) {
        return Optional.ofNullable(openUserCacheRepository.convert(userId)).orElse(0L);
    }

    /**
     * 合约
     *
     * @param templateId
     * @return
     */
    public Long template(Long templateId) {
        return Optional.ofNullable(openTemplateCacheRepository.convert(templateId)).orElse(0L);
    }

    /**
     * 客户
     *
     * @param customerId   老系统客户id
     * @param customerType 客户类型
     * @param tenantId     新系统客户所属租户id
     * @return
     */
    public SourceCustomer customer(Long customerId, Integer customerType, Long tenantId) {
        try {
            if (CustomerTypeEnum.MYSELF.getValue().equals(customerType)) {
                var mySelf = customerService.getMySelf(tenantId);
                return Optional.ofNullable(mySelf).map(c -> {
                    SourceCustomer customer = new SourceCustomer();
                    customer.setCustomerId(c.getCustomerId());
                    customer.setCustomerName(c.getName());
                    customer.setCustomerType(customerType);
                    return customer;
                }).orElse(null);
            } else {
                var id = openCustomerCacheRepository.convert(customerId);
                return Optional.ofNullable(id).map(x -> {
                    SourceCustomer customer = new SourceCustomer();
                    customer.setCustomerId(x);
                    customer.setCustomerType(customerType);
                    return customer;
                }).orElse(null);
            }
        } catch (Exception e) {
            logger.error("customerId:{}，customerType：{},tenantId：{},{}", customerId, customerType, tenantId, e.getMessage());
        }
        return null;
    }

    /**
     * 收货方
     *
     * @param createConsignee
     * @return
     */
    public ConsigneeDetailDTO consignee(CreateConsignee createConsignee) {
        return consigneeFacade.createConsignee(createConsignee);
    }

    /**
     * 物品
     *
     * @param goodsId
     * @return
     */
    public Long goods(Long goodsId) {
        return openGoodsCacheRepository.convert(goodsId);
    }

    /**
     * 承运商 id
     *
     * @param carrierId   承运方id (承运方类型为自己/自营时，承运方id为空)
     * @param carrierType 承运方类型
     * @param tenantId    新系统租户id
     * @return
     */
    public SourceCarrier carrier(Long carrierId, Integer carrierType, Long tenantId) {
        //线上、线下、个体司机
        if (CarrierTypeEnum.isOnline(carrierType) || CarrierTypeEnum.isOffline(carrierType)) {
            var id = openCarrierCacheRepository.convert(carrierId, carrierType);
            return Optional.ofNullable(id).map(x -> {
                SourceCarrier sourceCarrier = new SourceCarrier();
                sourceCarrier.setCarrierType(carrierType);
                sourceCarrier.setCarrierId(x);
                return sourceCarrier;
            }).orElse(null);

        }
        //自己 -- 没有调度时，默认为自己
        //自营司机 -- 司机 对应新系统的调度给自己,需要用租户id（自营司机的租户id与订单的租户id为同一个）去找类型为自己的承运商id
        if (CarrierTypeEnum.isMyself(carrierType) || CarrierTypeEnum.isDriver(carrierType)) {
            var mySelf = carrierService.getMySelf(tenantId);
            return Optional.ofNullable(mySelf).map(carrier -> {
                SourceCarrier sourceCarrier = new SourceCarrier();
                sourceCarrier.setCarrierId(carrier.getCarrierId());
                sourceCarrier.setCarrierName(carrier.getName());
                sourceCarrier.setCarrierContactName(carrier.getContactName());
                sourceCarrier.setCarrierPhone(carrier.getContactMobile());
                sourceCarrier.setCarrierType(carrier.getType());
                return sourceCarrier;
            }).orElse(null);
        }
        return null;
    }

    /**
     * 运单和拼车单
     *
     * @param transportId
     * @return
     */
    public Long transport(Long transportId) {
        return openTransportCacheRepository.convert(transportId);
    }

    public Long combine(Long combineId) {
        return openCombineCacheRepository.convert(combineId);
    }

    public Address area(AddressDTO address) {
        return areaFacade.getArea(address);
    }

    public <T extends OrderGoodsCreateDTO> void generateBatchNo(List<T> goodsList) {
        if (CollectionUtils.isEmpty(goodsList) || goodsList.size() == 1) {
            return;
        }
        var groups = goodsList.stream()
                .collect(Collectors.groupingBy(OrderGoodsCreateDTO::getGoodsId));
        AtomicInteger batch = new AtomicInteger(0);
        groups.forEach((goodsId, group) -> generateBatchNo(group, batch.addAndGet(1)));
        checkGoodsUnique(goodsList);
    }

    private <T extends OrderGoodsCreateDTO> void generateBatchNo(List<T> goodsList, int batch) {
        if (CollectionUtils.isEmpty(goodsList) || goodsList.size() == 1) {
            return;
        }
        for (int i = 0; i < goodsList.size(); i++) {
            var goods = goodsList.get(i);
            if (StringUtils.isEmpty(goods.getBatchNo())) {
                goods.setBatchNo(("#BA" + batch) + i);
            }
        }
    }

    private <T extends OrderGoodsCreateDTO> void checkGoodsUnique(List<T> goodsList) {
        List<String> goodsKeys = new ArrayList<>();
        goodsList.forEach(g -> {
            goodsKeys.add(buildKey(g));
        });

        goodsKeys.stream().distinct();
        for (int i = 0; i < goodsKeys.size(); i++) {
            String goodsKey = goodsKeys.get(i);
            var list = goodsList.stream().filter(g -> buildKey(g).equals(goodsKey)).collect(Collectors.toList());
            if (list.size() > 1) {
                for (int j = 0; j < list.size(); j++) {
                    var goods = list.get(j);
                    goods.setBatchNo(goods.getBatchNo() + "-#" + j);
                }
            }
        }
    }

    private String buildKey(OrderGoodsCreateDTO goods) {
        return goods.getGoodsId() + "#" + goods.getBatchNo();
    }
}
